[pull] master from cube-js:master - #651
Merged
Merged
Conversation
* fix(tesseract): plan multiplied measures per join tree, not per cube Multiplied measures were bucketed by owning cube and the whole bucket was then required to resolve to a single multi-fact join group, so two measures of one cube needing different join trees were refused with "Expected just one multi-fact join group". The key cube fixes the primary keys to deduplicate on and the join tree fixes the joins to build; neither determines the other. Group by both and emit one AggregateMultipliedSubquery per pair, mirroring the regular-measure branch, which already emits one CTE per join group. Trees are kept apart rather than merged so that a measure's value does not depend on which other measures share the query: merging sibling one-to-many branches introduces a cross product that neither original tree had. With a single group per cube the plan is unchanged, so queries that planned before are unaffected. * fix(tesseract): decompose calculated measures that reach other cubes Reaching a cube beyond its own makes a multiplied measure go through the measure-join subquery, which renders measures without their aggregate so the select above can re-aggregate them. A calculated measure has no aggregate of its own, so nothing was re-applied: its components lost theirs inside the subquery and the expression came out neither aggregated nor grouped. Mark such a measure composite instead, so its components travel in its place - each on the join tree its own definition asks for - and the expression is rebuilt above them. Only when the reach runs entirely through those components. A dimension dependency, a raw cube reference or no measure dependency at all leaves the measure with something to evaluate where it stands, and the select above has no cube joined in to evaluate it against; those keep going whole. * fix(tesseract): decompose calculated measures only where multiplied Decomposition exists to work around the measure-join subquery, which is built only for a measure that is multiplied by its join tree. Elsewhere the measure is read off a leaf-measure query that keeps its aggregate, and splitting it changes the answer: components can root at different cubes, so each divides by the rows its own leg sees rather than by the query's. Fail loudly instead of asserting when join grouping drops a measure of a bucket - a debug assertion leaves release builds emitting a plan with the measure missing. * test(tesseract): pin the same-cube split without a database The join grouping that multiplied measures rely on was only covered through Postgres-backed snapshots, so a run without the integration feature asserted nothing about it. Cover it where the grouping is decided. Record the denominator the non-multiplied control divides by: the ratio and the sibling total on that row are taken over different rows and are not meant to reconcile, which is not evident from the snapshot alone. * test(tesseract): assert the plan shape, not only the rows it returns The suite proved the numbers through Postgres but said nothing about how the plan reached them, so a run without the integration feature let a regression in the join grouping through as long as the query still planned. Count the deduplicating key subselects - one per (key cube, join tree) - and check whether a calculated measure's components get columns of their own. Both read structure out of the generated SQL rather than pinning it. * test(tesseract): state which planning strategy each case is holding A result snapshot records the numbers a query returned but not how it got them, so what these cases exist to pin was only in their names. Assert the leg count everywhere the strategy is the point: aggregated in place or deduplicated through the keys path, measures of one cube split across trees or grouped onto one. The mixed-shapes case wanted three legs and gets two - the calculated measure shares a tree with the bare aggregate, which is grouping working rather than a shape being missed. * test(tesseract): anchor the negated assertions and scope the grouping unit test A negated substring match passes both when a measure genuinely stays whole and when the alias convention the helper reads stops holding, at which point the assertion is inert without failing. Pair each with the positive case from the same query, which cannot hold unless the convention does. Say what the same-cube grouping unit test covers - the precondition callers slice on, not what any of them emits per group - and correct the sibling fan-out case, whose prose claimed a deduplication its own assertion denies. * fix(tesseract): refuse measures the measure subquery cannot carry Splitting a calculated measure into its components put the two halves of an expression on separate legs. When those legs root at different cubes they see different rows, so the ratio came out over a denominator its numerator never saw - answered, with no error, differently depending on whether the query carried a fan-out dimension. The split also never covered the shape it was written for: whether the measure subquery is built is decided per join group, while the condition for splitting was read off a single measure, so a calculated measure whose own dependencies stay inside its cube still lands in that subquery whenever a group member reaches out of it. Refuse instead. A measure carrying no aggregate of its own cannot survive a subquery that renders measures without theirs, whichever way it got there, so say that where the subquery is built and name the measure. Shapes that used to reach the database as SQL it rejects now stop at the planner. * fix(tesseract): check the measure subquery where it is rendered Refusing during logical planning ran ahead of the pre-aggregation optimizer, which may replace the whole node with a rollup and never render the subquery at all - a query a rollup answers was turned into an error. Move the check to where the subquery is actually built, past the point the override returns. Say what the user can do about it. The subquery is forced by whichever measure of the group reaches another cube, which need not be the one being refused, so telling that measure to be rewritten over its own cube asks for something it may already satisfy; ask for a separate query instead. * test(tesseract): cover the rollup that renders no measure subquery Nothing held the check to where it runs: the fixture declared no rollups, so moving it back ahead of the pre-aggregation optimizer left the suite green while turning a query a rollup answers into an error. Store one of the refused shapes and assert the usage. Name the reach rather than a sibling in the message. Four of the five refused shapes are single-measure queries where the measure reaches out itself, so asking for it in a separate query sends the user round in a circle; what forces the subquery is the reach, whoever carries it. Say that member expressions are left out deliberately, so the skip does not read as an oversight.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )